﻿using Swifter.Api.Types;
using Swifter.RW;
using Swifter.Tools;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;

#nullable enable

namespace Swifter.Api
{
    public abstract class BaseApplication
    {
        public const string ResourceDirectoryPathConfigurationName = "Resource_Directory_Path";

        public const int DefaultCapacity = 5000;
        public const int DefaultMaxCapacity = 50000000;
        public const long CleanMilliseconds = 20;
        public const long DefaultContextExpiredTicks = 30 * TimeSpan.TicksPerMinute; // 30 minutes
        public const long DefaultNewContextExpiredTicks = 1 * TimeSpan.TicksPerMinute; // 1 minutes
        public const long CleanIntervalTicks = 2 * TimeSpan.TicksPerHour; // 2 hours
        public const long ClientTimeErrorTicks = 5 * TimeSpan.TicksPerMinute; // 5 minutes
        public const long UpdateStateIntervalTicks = 5 * TimeSpan.TicksPerHour; // minutes

        public const string ParameterClientIdName = "ClientId";
        public const string ParameterDeviceIdName = "DeviceId";
        public const string ParameterLanguageName = "Language";

        public readonly string ApplicationName;
        public readonly Guid ApplicationId;
        public readonly int MaxCapacity = 50000000;

        readonly Dictionary<string, string> application_urls;
        readonly Dictionary<string, CommandGroup> commands_cache;
        readonly object commands_cache_Lock;
        readonly ConcurrentDictionary<string, bool> role_commands_cache;
        readonly object role_commands_cache_lock;
        readonly ConcurrentDictionary<string, ICommandProcessor> processor_cache;
        readonly ThreadLocal<SignBuilder> sign_builders;
        readonly object update_state_lock;
        readonly Dictionary<string, Type> types;
        readonly Dictionary<Type, string> types_name_map;
        readonly object types_lock;
        readonly ConcurrentDictionary<string, ConcurrentDictionary<string, RuntimeCacheInfo>> caches;


        ContextInfo?[] contexts;
        readonly object contexts_lock;

        int contexts_insert_index;
        int contexts_clean_index;

        long last_clean_timestamp;
        long last_update_state_timestamp;

        FreedLinkNode? contexts_freed;
        int contexts_freed_count;


        public TimeSpan ContextExpiredInterval { get; set; }

        public TimeSpan NewContextExpiredInterval { get; set; }

        public BaseApplication(string name, Guid applicationId, int capacity, int maxCapacity)
        {
            ApplicationName = name;
            ApplicationId = applicationId;

            contexts = new ContextInfo?[capacity];
            contexts_lock = new object();

            commands_cache = new Dictionary<string, CommandGroup>();
            commands_cache_Lock = new object();
            processor_cache = new ConcurrentDictionary<string, ICommandProcessor>();
            role_commands_cache = new ConcurrentDictionary<string, bool>();
            role_commands_cache_lock = new object();
            sign_builders = new ThreadLocal<SignBuilder>(() => new SignBuilder());
            application_urls = new Dictionary<string, string>();
            types = new Dictionary<string, Type>();
            types_name_map = new Dictionary<Type, string>();
            types_lock = new object();
            caches = new ConcurrentDictionary<string, ConcurrentDictionary<string, RuntimeCacheInfo>>();

            update_state_lock = new object();

            last_clean_timestamp = Stopwatch.GetTimestamp();

            ContextExpiredInterval = new TimeSpan(DefaultContextExpiredTicks);
            NewContextExpiredInterval = new TimeSpan(DefaultNewContextExpiredTicks);

            MaxCapacity = maxCapacity;

            Initialize();

            ImportNativeCommands();
            RegisteTypes();
        }

        /// <summary>
        /// 添加指定协议的服务器 Url。
        /// </summary>
        /// <param name="protocol">协议名称</param>
        /// <param name="url">服务器 Url</param>
        public void AddUrl(string protocol, string url)
        {
            lock (update_state_lock)
            {
                if (last_update_state_timestamp != 0)
                {
                    throw new InvalidOperationException("This method is not allowed execution after initial .");
                }

                application_urls.Add(protocol, url);
            }
        }

        public void AddCommandProcessor(string name, ICommandProcessor processor)
        {
            if (!processor_cache.TryAdd(name ?? throw new ArgumentNullException(nameof(name)), processor ?? throw new ArgumentNullException(nameof(processor))))
            {
                throw new InvalidOperationException($"Processor : '{name}' already exists.");
            }
        }

        private void ImportNativeCommands()
        {
            foreach (var assembly in GetAssemblies())
            {
                foreach (var type in assembly.GetTypes())
                {
                    if (typeof(INativeCommandInfo).IsAssignableFrom(type) && !type.IsAbstract)
                    {
                        try
                        {
                            var instance = (INativeCommandInfo)Activator.CreateInstance(type);

                            AddNativeCommand(instance);
                        }
                        catch (Exception)
                        {
                            Log(nameof(BaseApplication), $"Import native command failed! Command type : '{type}'.");
                        }
                    }
                }
            }
        }

        private void RegisteTypes()
        {
            internal_registe_type(typeof(bool));
            internal_registe_type(typeof(byte));
            internal_registe_type(typeof(sbyte));
            internal_registe_type(typeof(short));
            internal_registe_type(typeof(ushort));
            internal_registe_type(typeof(int));
            internal_registe_type(typeof(uint));
            internal_registe_type(typeof(long));
            internal_registe_type(typeof(ulong));
            internal_registe_type(typeof(float));
            internal_registe_type(typeof(double));
            internal_registe_type(typeof(decimal));
            internal_registe_type(typeof(char));
            internal_registe_type(typeof(string));
            internal_registe_type(typeof(DateTime));
            internal_registe_type(typeof(TimeSpan));
            internal_registe_type(typeof(DateTimeOffset));
            internal_registe_type(typeof(Guid));

            internal_registe_type(typeof(bool?));
            internal_registe_type(typeof(byte?));
            internal_registe_type(typeof(sbyte?));
            internal_registe_type(typeof(short?));
            internal_registe_type(typeof(ushort?));
            internal_registe_type(typeof(int?));
            internal_registe_type(typeof(uint?));
            internal_registe_type(typeof(long?));
            internal_registe_type(typeof(ulong?));
            internal_registe_type(typeof(float?));
            internal_registe_type(typeof(double?));
            internal_registe_type(typeof(decimal?));
            internal_registe_type(typeof(char?));
            internal_registe_type(typeof(DateTime?));
            internal_registe_type(typeof(TimeSpan?));
            internal_registe_type(typeof(DateTimeOffset?));
            internal_registe_type(typeof(Guid?));


            foreach (var assembly in GetAssemblies())
            {
                foreach (var type in assembly.GetTypes())
                {
                    if (typeof(ITypeInfo).IsAssignableFrom(type) && !type.IsAbstract)
                    {
                        try
                        {
                            internal_registe_type(type);
                        }
                        catch (Exception)
                        {
                            Log(nameof(BaseApplication), $"Import type failed! Type : '{type}'.");
                        }
                    }
                }
            }

            void internal_registe_type(Type type)
            {
                InternalRegisteType(GetTypeName(type), type);
            }
        }

        private void AddNativeCommand(INativeCommandInfo nativeCommandInfo)
        {
            var commandInfo = new RuntimeCommandInfo(nativeCommandInfo.Name, nativeCommandInfo.Version, null)
            {
                Invoker = nativeCommandInfo,
                AfterTriggers = Array.Empty<string>(),
                BeforeTriggers = Array.Empty<string>(),
            };

            AddRuntimeCommandInfo(commandInfo);
        }

        public string MakeToken(ContextParameters parameters)
        {
            if (!parameters.CommandParameters.TryGetValue(ParameterClientIdName, out var clientId))
            {
                throw new DeveloperException($"Parameter : '{ParameterClientIdName}' cannnot be null.");
            }

            if (!parameters.CommandParameters.TryGetValue(ParameterClientIdName, out var deviceId))
            {
                throw new DeveloperException($"Parameter : '{ParameterClientIdName}' cannnot be null.");
            }

            if (!parameters.CommandParameters.TryGetValue(ParameterClientIdName, out var language))
            {
                throw new DeveloperException($"Parameter : '{ParameterClientIdName}' cannnot be null.");
            }

            if (Stopwatch.GetTimestamp() - last_clean_timestamp >= CleanIntervalTicks || contexts_insert_index >= contexts.Length)
            {
                InternalCleanContexts();
            }

        Loop:

            while (true)
            {
                var freed = Volatile.Read(ref contexts_freed);

                if (freed == null)
                {
                    break;
                }

                if (freed.Index >= 0 && freed.Index <= contexts.Length && Interlocked.CompareExchange(ref contexts_freed, freed.Next, freed) == freed)
                {
                    Interlocked.Decrement(ref contexts_freed_count);

                    return CreateToken(freed.Index);
                }
            }


            if (contexts_insert_index >= MaxCapacity)
            {
                if (Volatile.Read(ref contexts_freed) != null)
                {
                    goto Loop;
                }

                throw new ReApplicationException("This application is full.");
            }

            var index = Interlocked.Increment(ref contexts_insert_index) - 1;

            if (index < 0)
            {
                goto Loop;
            }

            if (index >= contexts.Length)
            {
                lock (contexts_lock)
                {
                    if (index >= contexts.Length)
                    {
                        var newArray = contexts;

                        Array.Resize(ref newArray, Math.Min(index + newArray.Length + DefaultCapacity, MaxCapacity));

                        contexts = newArray;
                    }
                }
            }

            return CreateToken(index);

            string CreateToken(int contextId)
            {
                var contextInfo = new ContextInfo(
                    this, 
                    ValueInterface<Guid>.ReadValue(clientId), 
                    ValueInterface<Guid>.ReadValue(deviceId), 
                    ValueInterface<string>.ReadValue(language),
                    parameters.IP
                    );

                contexts[contextId] = contextInfo;

                return new TokenInfo()
                {
                    ApplicationId = ApplicationId,
                    ContextId = contextId,
                    ValidatorHigh = contextInfo.validator_high,
                    ValidatorLow = contextInfo.validator_low
                }.ToString();
            }
        }

        public void FreeToken(string token)
        {
            if (TokenInfo.TryParse(token, out var tokenInfo))
            {
                if (tokenInfo.ApplicationId != ApplicationId)
                {
                    throw new UnauthorizedException($"The {nameof(token)} does not belong to this server.");
                }

                if (tokenInfo.ContextId >= 0 &&
                    tokenInfo.ContextId < contexts.Length &&
                    contexts[tokenInfo.ContextId] is ContextInfo contextInfo &&
                    tokenInfo.ValidatorHigh == contextInfo.validator_high &&
                    tokenInfo.ValidatorHigh == contextInfo.validator_high)
                {
                    InternalFreeToken(tokenInfo.ContextId);
                }
            }
            else
            {
                throw new UnauthorizedException($"The {nameof(token)} is wrong.");
            }
        }

        private void CheckSign(ContextParameters parameters, ContextInfo? context = null)
        {
            var sign_builder = sign_builders.Value;

            sign_builder.Clear();

            sign_builder.Append(nameof(ContextParameters.ClientTime), parameters.ClientTime);
            sign_builder.Append(nameof(ContextParameters.Command), parameters.Command);
            sign_builder.Append(nameof(ContextParameters.ContentType), parameters.ContentType);
            sign_builder.Append(nameof(ContextParameters.Token), parameters.Token);

            var sorted_parameters = parameters.CommandParameters.OrderBy(item => item.Key);

            foreach (var item in sorted_parameters)
            {
                sign_builder.Append(item.Key, item.Value.Value);
            }

            if (context != null)
            {
                sign_builder.Append(ParameterClientIdName, context.client_id);
                sign_builder.Append(ParameterDeviceIdName, context.device_id);
            }

            var sign = sign_builder.ComputeSign();

            sign_builder.Clear();

            if (!string.Equals(sign, parameters.Sign, StringComparison.InvariantCultureIgnoreCase))
            {
                throw new DeveloperException("Sign is incorrect!");
            }
        }

        private ApplicationState GetApplicationState()
        {
            var directory = new DirectoryInfo(ValueInterface<string>.ReadValue(GetGlobalParameter(ResourceDirectoryPathConfigurationName)));

            Helper.GetDickInfo(directory, out var total_dick, out var used_dick);

            // TODO: Memory and CPU.

            return new ApplicationState
            {
                MaxCapacity = MaxCapacity,
                Count = contexts_insert_index - contexts_freed_count,
                ApplicationId = ApplicationId,
                Name = ApplicationName,
                ApplicationUrls = new Dictionary<string, string>(application_urls),
                TotalDick = total_dick,
                UsedDick = used_dick,
            };
        }

        /// <summary>
        /// 选择一个相对客户端较合适的服务器
        /// </summary>
        /// <returns></returns>
        private string[] SelectApplicationUrls(ContextParameters parameters)
        {
            var application = GetApplicationState();
            var applications = GetApplicationStates();

            if (applications.Length == 0)
            {
                throw new DeveloperException("Need more distributed servers!");
            }

            Array.Sort(applications, (x, y) => GetComparison(x).CompareTo(GetComparison(y)));

            var result = applications
                .Where(item => GetComparison(item) < GetComparison(application) && item.ApplicationUrls.ContainsKey(parameters.Protocol))
                .Select(item => item.ApplicationUrls[parameters.Protocol]).ToArray();

            if (result.Length == 0)
            {
                throw new DeveloperException("Need more distributed servers!");
            }

            return result;

            static int GetComparison(ApplicationState state)
            {
                if (state == null)
                {
                    return int.MaxValue;
                }

                if (state.TotalDick > 0 && ((double)state.UsedDick / state.TotalDick) >= 0.95)
                {
                    return int.MaxValue;
                }
                if (state.TotalFrequency > 0 && ((double)state.UsedFrequency / state.TotalFrequency) >= 0.95)
                {
                    return int.MaxValue;
                }
                if (state.TotalMemory > 0 && ((double)state.UsedMemory / state.TotalMemory) >= 0.95)
                {
                    return int.MaxValue;
                }

                if (((double)state.MaxCapacity / state.Count) >= 0.9)
                {
                    return int.MaxValue;
                }

                return (state.MaxCapacity - state.Count);
            }
        }

        protected void Process(ContextParameters parameters)
        {
            var stopwatch = Stopwatch.StartNew();
            var results = new ContextResults();

            ContextInfo? contextInfo = null;

            try
            {
                if (Math.Abs((DateTimeOffset.Now - parameters.ClientTime).Ticks) > ClientTimeErrorTicks)
                {
                    throw new ReTimeException("Inaccurate client time.");
                }

                if (parameters.Command == null)
                {
                    throw new DeveloperException($"Parameter: '{nameof(parameters.Command)}' cannot be null.");
                }

                if (parameters.ContentType == null)
                {
                    throw new DeveloperException($"Parameter: '{nameof(parameters.ContentType)}' cannot be null.");
                }

                CheckSign(parameters, contextInfo);

                if (Stopwatch.GetTimestamp() - last_update_state_timestamp >= UpdateStateIntervalTicks)
                {
                    var update_state = false;

                    lock (update_state_lock)
                    {
                        if (Stopwatch.GetTimestamp() - last_update_state_timestamp >= UpdateStateIntervalTicks)
                        {
                            last_update_state_timestamp = Stopwatch.GetTimestamp();

                            update_state = true;
                        }
                    }

                    if (update_state)
                    {
                        UpdateApplicationState(GetApplicationState());
                    }
                }

                // 基础命令
                switch (parameters.Command)
                {
                    case "MakeToken":

                        if (!string.IsNullOrEmpty(parameters.Token))
                        {
                            FreeToken(parameters.Token);
                        }

                        finish(new { Token = MakeToken(parameters) });

                        break;
                    default:

                        contextInfo = GetContext(parameters.Token);

                        contextInfo.Process(parameters, finish);

                        break;
                }
            }
            catch (Exception e)
            {
                finish(e);
            }

            void finish(object result)
            {
                if (result is RuntimeCacheInfo cacheInfo)
                {
                    results.CacheTime = cacheInfo.CacheTime;

                    result = cacheInfo.Data;
                }

                if (result is Exception e)
                {
                    ProcessException(contextInfo, parameters, e, results);
                }

                results.Data = result;

                results.ServerTime = DateTimeOffset.Now;
                results.UsedTime = stopwatch.ElapsedTicks;

                Finish(contextInfo, parameters, results);
            }
        }

        /// <summary>
        /// 处理异常信息返回值。
        /// </summary>
        /// <param name="contextInfo">上下文</param>
        /// <param name="parameters">上下文参数</param>
        /// <param name="e">异常</param>
        /// <param name="results">返回值信息</param>
        internal protected virtual void ProcessException(ContextInfo contextInfo, ContextParameters parameters, Exception e, ContextResults results)
        {
            if (e is ReApplicationException re_app)
            {
                results.Code = re_app.Code;
                results.Message = e.Message;
                results.Data = new { ApplicationUrls = SelectApplicationUrls(parameters) };
            }
            else if (e is BaseException base_e)
            {
                results.Code = base_e.Code;
                results.Message = e.Message;
                results.Data = base_e.Data;
            }
            else
            {
                results.Code = (int)ReturnCodes.Error;
                results.Message = e.Message;
                results.ExceptionType = e.GetType().Name;
                results.Data = e.Data;
            }
        }

        /// <summary>
        /// 应用程序初始化方法。
        /// </summary>
        protected abstract void Initialize();

        /// <summary>
        /// 当程序处理完成后所执行的完成方法。
        /// </summary>
        /// <param name="contextInfo">当前上下文</param>
        /// <param name="parameters">上下文参数</param>
        /// <param name="results">程序处理得到的结果</param>
        protected abstract void Finish(ContextInfo contextInfo, ContextParameters parameters, ContextResults results);

        /// <summary>
        /// 输出程序日志。
        /// </summary>
        /// <param name="title">日志标题</param>
        /// <param name="msg">日志文本</param>
        public abstract void Log(string title, string msg);

        /// <summary>
        /// 获取应用程序的参数。
        /// </summary>
        /// <typeparam name="T">指定类型</typeparam>
        /// <param name="name">指定名称</param>
        /// <param name="contextInfo">当前上下文</param>
        /// <param name="parameters">上下文参数</param>
        /// <returns></returns>
        internal protected abstract ValueCopyer GetApplicationParameter(string name, ContextInfo contextInfo, ContextParameters parameters);

        /// <summary>
        /// 获取全局配置项的值。
        /// </summary>
        /// <typeparam name="T">指定类型</typeparam>
        /// <param name="name">指定名称</param>
        /// <returns></returns>
        internal protected abstract ValueCopyer GetGlobalParameter(string name);

        /// <summary>
        /// 获取指定角色允许执行的所有命令。
        /// </summary>
        /// <param name="roleId"></param>
        /// <returns></returns>
        internal protected abstract string[] GetRoleCommands(int roleId);

        /// <summary>
        /// 获取所有的分布式服务器的实时状态。
        /// </summary>
        /// <returns></returns>
        internal protected abstract ApplicationState[] GetApplicationStates();

        /// <summary>
        /// 更新当前服务器实时状态。
        /// </summary>
        /// <param name="state"></param>
        internal protected abstract void UpdateApplicationState(ApplicationState state);

        private static string GetRoleCommandKey(int roleId, string commandName)
        {
            unsafe
            {
                var role_command = "        _" + commandName;

                fixed (char* pRole = role_command)
                {
                    NumberHelper.Hex.ToString((uint)(roleId & uint.MaxValue), 8, pRole);
                }

                return role_command;
            }
        }

        public bool CheckCommandAuthorization(int roleId, string commandName)
        {
            var role_command = GetRoleCommandKey(roleId, commandName);

            if (role_commands_cache.TryGetValue(role_command, out var result))
            {
                return result;
            }

            lock (role_commands_cache_lock)
            {
                if (role_commands_cache.TryGetValue(role_command, out result))
                {
                    return result;
                }

                var commands = GetRoleCommands(roleId);

                foreach (var item in commands)
                {
                    role_commands_cache.TryAdd(GetRoleCommandKey(roleId, item), true);
                }

                if (role_commands_cache.TryGetValue(role_command, out result))
                {
                    return result;
                }

                role_commands_cache.TryAdd(GetRoleCommandKey(roleId, commandName), false);

                return false;
            }
        }

        internal void EnterLock(string lock_key)
        {
        }

        internal void ExitLock(string lock_key)
        {

        }

        internal string ProcessKey(string key, ContextInfo contextInfo, ContextParameters parameters)
        {
            return key;
        }

        internal string GetCacheKey(ContextParameters parameters)
        {
            var sign_buider = sign_builders.Value;

            sign_buider.Clear();

            sign_buider.Append(nameof(ContextParameters.Command), parameters.Command);

            foreach (var item in parameters.CommandParameters)
            {
                sign_buider.Append(item.Key, item.Value.Value);
            }

            var parameters_key = sign_buider.ComputeSign();

            sign_buider.Clear();

            return parameters_key;
        }

        internal bool GetCacheInfo(string cache_key, ContextParameters parameters, out RuntimeCacheInfo? cache_info)
        {
            cache_info = default;

            return caches.TryGetValue(cache_key, out var curr_caches) && curr_caches.TryGetValue(GetCacheKey(parameters), out cache_info);
        }

        internal void SetCacheInfo(string cache_key, ContextParameters parameters, RuntimeCacheInfo cache_info)
        {
            caches.GetOrAdd(cache_key, create_caches)[GetCacheKey(parameters)] = cache_info;

            static ConcurrentDictionary<string, RuntimeCacheInfo> create_caches(string add_key)
            {
                return new ConcurrentDictionary<string, RuntimeCacheInfo>();
            }
        }

        /// <summary>
        /// 尝试获取指定名称，并且版本小于或等于指定版本的命令信息。
        /// </summary>
        /// <param name="name">指定名称</param>
        /// <param name="version">指定版本</param>
        /// <returns>命令信息，获取失败则返回 null</returns>
        protected abstract CommandInfo TryGetCommand(string name, Version version);

        private void AddRuntimeCommandInfo(RuntimeCommandInfo commandInfo)
        {
            lock (commands_cache_Lock)
            {
                if (!commands_cache.TryGetValue(commandInfo.Name, out var group))
                {
                    group = new CommandGroup(commandInfo.Name);

                    commands_cache.Add(commandInfo.Name, group);
                }

                group.Commands.Add(commandInfo);

                group.Commands.Sort((x, y) => y.Version.CompareTo(x.Version));
            }
        }

        /// <summary>
        /// 获取指定名称，并且版本小于或等于指定版本的命令信息。
        /// </summary>
        /// <param name="name"></param>
        /// <param name="version"></param>
        /// <returns>返回运行时命令信息</returns>
        public RuntimeCommandInfo GetCommand(string name, Version version)
        {
            if (!commands_cache.TryGetValue(name, out var commands))
            {
                lock (commands_cache_Lock)
                {
                    if (!commands_cache.TryGetValue(name, out commands))
                    {
                        return InternalGetCommand();
                    }
                }
            }

            if (version == Versions.Debug)
            {
                return commands.Commands.First();
            }

            foreach (var item in commands.Commands)
            {
                if (item.Version <= version)
                {
                    return item;
                }
            }

            return InternalGetCommand();

            RuntimeCommandInfo InternalGetCommand()
            {
                lock (commands_cache_Lock)
                {
                    if (TryGetCommand(name, version) is CommandInfo commandInfo)
                    {
                        if (processor_cache.TryGetValue(commandInfo.Type, out var processor))
                        {
                            var runtimeCommandInfo = processor.Build(this, commandInfo);

                            GetTriggers(commandInfo.Name, out runtimeCommandInfo.BeforeTriggers, out runtimeCommandInfo.AfterTriggers);

                            AddRuntimeCommandInfo(runtimeCommandInfo);

                            return runtimeCommandInfo;
                        }
                        else
                        {
                            throw new DeveloperException("Command processor not found.");
                        }
                    }
                    else
                    {
                        throw new DeveloperException("Command not found.");
                    }
                }
            }
        }

        /// <summary>
        /// 获取指定命令的前置触发器和后置触发器。
        /// </summary>
        /// <param name="commandName">命令名称</param>
        /// <param name="beforeTriggers">返回前置触发器，不能返回 null</param>
        /// <param name="afterTriggers">返回后置触发器，不能返回 null</param>
        protected abstract void GetTriggers(string commandName, out string[] beforeTriggers, out string[] afterTriggers);

        /// <summary>
        /// 通过 Type 的名称获取 Type 信息。
        /// </summary>
        /// <param name="name">Type 名称</param>
        /// <returns>返回 Type 信息</returns>
        public Type GetType(string name)
        {
            if (types.TryGetValue(name, out var type))
            {
                return type;
            }

            throw new DeveloperException($"Type : {name} not found.");
        }

        /// <summary>
        /// 获取指定类型的名称。
        /// </summary>
        /// <param name="type">指定类型</param>
        /// <returns>返回类型的名称</returns>
        public string GetTypeName(Type type)
        {
            if (types_name_map.TryGetValue(type, out var name))
            {
                return name;
            }

            if (Nullable.GetUnderlyingType(type) is Type valueType)
            {
                return $"{GetTypeName(valueType)}?";
            }

            if (type.IsGenericType)
            {
                var nameBuilder = new StringBuilder();

                nameBuilder.Append(type.Name);
                nameBuilder.Append("<");

                foreach (var item in type.GetGenericArguments())
                {
                    nameBuilder.Append(GetTypeName(item));
                    nameBuilder.Append(",");
                }

                --nameBuilder.Length;

                nameBuilder.Append(">");

                return nameBuilder.ToString();
            }

            if (type.IsArray)
            {
                var nameBuilder = new StringBuilder();

                nameBuilder.Append(GetTypeName(type.GetElementType()));
                nameBuilder.Append("[");

                for (int i = 0; i < type.GetArrayRank(); i++)
                {
                    nameBuilder.Append(",");
                }

                --nameBuilder.Length;

                nameBuilder.Append("]");
            }

            return type.Name;
        }

        /// <summary>
        /// 注册类型。
        /// </summary>
        /// <param name="name">类型名称</param>
        /// <param name="type">类型</param>
        public void RegisteType(string name, Type type)
        {
            lock (types_lock)
            {
                InternalRegisteType(name, type);
            }
        }

        private void InternalRegisteType(string name, Type type)
        {
            types.Add(name, type);
            types_name_map.Add(type, name);
        }

        /// <summary>
        /// 获取用于加载 Application 的程序集信息。
        /// </summary>
        /// <returns>返回程序集集合</returns>
        public virtual IEnumerable<Assembly> GetAssemblies()
        {
            yield return typeof(BaseApplication).Assembly;

            yield return GetType().Assembly;
        }

        void InternalFreeToken(int contextId)
        {
            using (Interlocked.Exchange(ref contexts[contextId], default))
            {
                var freed = new FreedLinkNode() { Index = contextId };

                while (true)
                {
                    freed.Next = Volatile.Read(ref contexts_freed);

                    if (Interlocked.CompareExchange(ref contexts_freed, freed, freed.Next) == freed.Next)
                    {
                        Interlocked.Increment(ref contexts_freed_count);

                        break;
                    }
                }
            }
        }

        void InternalCleanContexts()
        {
            lock (contexts_lock)
            {
                if (Stopwatch.GetTimestamp() - last_clean_timestamp < CleanIntervalTicks)
                {
                    return;
                }

                var stopwatch = Stopwatch.StartNew();

                var context_expired_ticks = ContextExpiredInterval.Ticks;

                last_clean_timestamp = Stopwatch.GetTimestamp();

                while (stopwatch.ElapsedMilliseconds < CleanMilliseconds)
                {
                    for (int i = 0; i < 1000 && contexts_clean_index < contexts_insert_index && contexts_clean_index < contexts.Length; i++, contexts_clean_index++)
                    {
                        if (contexts[contexts_clean_index] is ContextInfo contextInfo && last_clean_timestamp - contextInfo.last_process_timestamp >= last_clean_timestamp)
                        {
                            InternalFreeToken(contexts_clean_index);
                        }
                    }

                    if (contexts_clean_index >= contexts_insert_index || contexts_clean_index >= contexts.Length)
                    {
                        contexts_clean_index = 0;

                        break;
                    }
                }
            }
        }

        public ContextInfo GetContext(string token)
        {
            if (TokenInfo.TryParse(token, out var tokenInfo))
            {
                if (tokenInfo.ApplicationId != ApplicationId)
                {
                    throw new UnauthorizedException($"The {nameof(token)} does not belong to this server.");
                }

                if (tokenInfo.ContextId >= 0 &&
                    tokenInfo.ContextId < contexts.Length &&
                    contexts[tokenInfo.ContextId] is ContextInfo contextInfo &&
                    tokenInfo.ValidatorHigh == contextInfo.validator_high &&
                    tokenInfo.ValidatorHigh == contextInfo.validator_high)
                {
                    return contextInfo;
                }

                throw new ReTokenException($"The {nameof(token)} has expired.");
            }
            else
            {
                throw new UnauthorizedException($"The {nameof(token)} is wrong.");
            }
        }

        [StructLayout(LayoutKind.Sequential)]
        struct TokenInfo
        {
            static readonly ThreadLocal<byte[]> _500Bytes = new ThreadLocal<byte[]>(() => new byte[500]);

            public Guid ApplicationId { get; set; }

            public int ContextId { get; set; }

            public Guid ValidatorHigh { get; set; }

            public Guid ValidatorLow { get; set; }

            public static bool TryParse(string text, out TokenInfo token)
            {
                token = default;

                if (text == null)
                {
                    return false;
                }

                var bytes = _500Bytes.Value;

                if (Convert.TryFromBase64String(text, new Span<byte>(bytes), out var bytesWritten))
                {
                    for (int i = 0; i < bytesWritten; i++)
                    {
                        bytes[i] += (byte)(12 * i + 18);
                    }

                    ref var destination = ref Unsafe.As<TokenInfo, byte>(ref token);

                    var count = 0;

                    for (int i = 0; i < bytesWritten; ++count, i += (count * 18) % 12 + 1)
                    {
                        Unsafe.Add(ref destination, count) = bytes[i];
                    }

                    if (count == Unsafe.SizeOf<TokenInfo>())
                    {
                        // TODO: BitConverter.IsLittleEndian

                        return true;
                    }
                }

                return false;
            }

            public override string ToString()
            {
                var bytes = _500Bytes.Value;

                var seed = DateTime.Now.Ticks % 19971218;

                for (int i = 0; i < bytes.Length; i++)
                {
                    bytes[i] = (byte)(i * seed);
                }

                ref var source = ref Unsafe.As<TokenInfo, byte>(ref this);

                // TODO: BitConverter.IsLittleEndian

                var count = 0;

                for (int i = 0; i < Unsafe.SizeOf<TokenInfo>(); i++, count += (i * 18) % 12 + 1)
                {
                    bytes[count] = Unsafe.Add(ref source, i);
                }

                for (int i = 0; i < count; i++)
                {
                    bytes[i] -= (byte)(12 * i + 18);
                }

                return Convert.ToBase64String(new ReadOnlySpan<byte>(bytes, 0, count));
            }
        }

        sealed class FreedLinkNode
        {
            public int Index;

            public FreedLinkNode? Next;
        }

        sealed class CommandGroup
        {
            public CommandGroup(string name)
            {
                Name = name;

                Commands = new List<RuntimeCommandInfo>();
            }

            public string Name { get; set; }

            public List<RuntimeCommandInfo> Commands { get; set; }
        }
    }
}
